package com.mamingchao.scratch_story.service.impl;

import com.viewhigh.oes.sc.gatesplatform.kit.id.IdUtil;
import individual.mamingchao.scratch_story.common.CodeMsg;
import individual.mamingchao.scratch_story.dao.ActionEventsMapper;
import individual.mamingchao.scratch_story.dao.UserChapterEventsRefMapper;
import individual.mamingchao.scratch_story.dao.UserMapper;
import individual.mamingchao.scratch_story.dto.ActionEvent;
import individual.mamingchao.scratch_story.dto.ActionLine;
import individual.mamingchao.scratch_story.dto.UserChapterEvents;
import individual.mamingchao.scratch_story.dto.UserInfo;
import individual.mamingchao.scratch_story.entity.ActionEventDict;
import individual.mamingchao.scratch_story.entity.ActionEvents;
import individual.mamingchao.scratch_story.entity.User;
import individual.mamingchao.scratch_story.entity.UserChapterEventsRef;
import individual.mamingchao.scratch_story.exception.GenericResponse;
import individual.mamingchao.scratch_story.service.UserService;
import individual.mamingchao.scratch_story.util.RedisTools;
import individual.mamingchao.scratch_story.util.SignatureUtil;
import individual.mamingchao.scratch_story.vo.LoginVo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by mamingchao on 2020/12/25.
 */
@Service
public class UserServiceImpl implements UserService{

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private UserChapterEventsRefMapper userChapterEventsRefMapper;

    @Autowired
    private ActionEventsMapper actionEventsMapper;

    @Autowired
    private static RedisTemplate<String,Object> redisTemplate;

    /**
     * 注册用户
     * password 穿过来的必须是 md5后的
     *
     * @param userInfo
     * @return
     */
    @Override
    public boolean register(UserInfo userInfo) {

        int num = this.userMapper.insertSelective(this.convertUserDto2User(userInfo, true));
        return num == 1;
    }

    /**
     * 用户登录
     *
     * @param account     账户
     * @param md5Password md5后的用户密码
     * @param signature   SHA-1(key1=value1&key2=value2&salt)
     * @return
     */
    @Override
    public GenericResponse login(String account, String md5Password, String signature, int maxGameMinutes) {
        if (SignatureUtil.isValid(signature,account,md5Password)) {

            User userInfo = userMapper.getByAccountAndPassword(account,md5Password);
            if (userInfo == null){
                return new GenericResponse(CodeMsg.NO_AUTH);
            }

            LoginVo vo = new LoginVo();
            BeanUtils.copyProperties(userInfo,vo);
            String token = IdUtil.generateWithoutMiddleLineUUId();

            RedisTools.setWithTTL(token, userInfo,maxGameMinutes);
            vo.setToken(token);

            return new GenericResponse(vo);

        }
        return null;
    }

    /**
     * 用户 登出
     *
     * @param token 用户token
     * @return
     */
    @Override
    public boolean loginOut(String token) {
        if (RedisTools.isTokenValid(token)) {
            return redisTemplate.delete(token);
        }
        return false;
    }

    /**
     * 返回更新后的user信息
     *
     * @param user
     * @return 更新成功返回 新的user info；更新失败返回null；
     */
    @Override
    public GenericResponse updUsr(UserInfo user, String token) {
        if (RedisTools.isTokenValid(token)) {
            return new GenericResponse(CodeMsg.FORBIDDEN);
        }

        LoginVo vo = new LoginVo();
        BeanUtils.copyProperties(user,vo);

        redisTemplate.opsForValue().set(token,vo);

        int num = this.userMapper.updateByPrimaryKeySelective(this.convertUserDto2User(user,false));
        return num == 1 ? new GenericResponse(user) : new GenericResponse(CodeMsg.FAILED);
    }

    /**
     * 通过token获取 用户信息
     *
     * @param token
     * @return
     */
    @Override
    public GenericResponse getUserInfo(String token) {
        return new GenericResponse((LoginVo)redisTemplate.opsForValue().get(token));
    }

    /**
     * 通过用户编码 章节编码 获取 顺序的章节时间链表
     * 顺序就是 order by chapter_no
     *
     * 用来查询 用户 之前用户保存的 故事章节事件列表
     *
     * @param userCode
     * @param chapterCode
     * @return
     */
    @Override
    public LinkedList<ActionEvent> queryByUserChapterCode(String userCode, String chapterCode) {

        LinkedList<ActionEvent> result = new LinkedList<>();

        LinkedList<ActionEventDict> events = this.userMapper.queryByUserChapterCode(userCode, chapterCode);

        events.stream().forEach(item ->{
            ActionEvent event = new ActionEvent();
            BeanUtils.copyProperties(item,event);
            result.add(event);
        });
        return result;
    }

    /**
     * 用户 保存章节的事件链表
     * 这里包括 新增保存、更新保存
     *
     * 在用户 完成本章游戏 保存的时候，调用此接口
     * @param userChapterEvents
     * @return
     */
    @Override
    @Transactional(propagation=Propagation.REQUIRED)
    public boolean saveUserChapterEventList(UserChapterEvents userChapterEvents) {

        // 更新 user chapter 信息 为 have done 状态
        UserChapterEventsRef ucer = new UserChapterEventsRef();
        BeanUtils.copyProperties(userChapterEvents,ucer);
        int num1 = this.userChapterEventsRefMapper.haveBeenDone(userChapterEvents.getId());

        // 更新 用户故事章节的 事件链表（先全部删除，在全部新增）
        // 这里是删除，通过 userChapterRefCode
        Assert.notEmpty(userChapterEvents.getActionLines(),"本章故事【" + userChapterEvents.getChapterCode() +"】事件链表是空");
        this.actionEventsMapper.delByUserChapterRefCode(userChapterEvents.getCode());

        // 这里是新增
        List<ActionEvents> eventsLine = new ArrayList<>();
        int eventOrderNum = 0;

        for (ActionLine al : userChapterEvents.getActionLines()) {
            ActionEvents ae = new ActionEvents();
            ae.setCode(IdUtil.generateWithoutMiddleLineUUId());
            ae.setEventDictCode(al.getEventDictCode());
            ae.setEventOrder(eventOrderNum ++);
            ae.setChapterCode(userChapterEvents.getChapterCode());

            eventsLine.add(ae);
        }

        int num2 = this.actionEventsMapper.batchSave(eventsLine);

        return num1 ==1 && num2 == userChapterEvents.getActionLines().size();
    }


    /**
     * 将 UserDto 转化成 User
     * @param dto
     * @return
     */
    private User convertUserDto2User(UserInfo dto, boolean gcParam) {
        User user = new User();
        BeanUtils.copyProperties(dto,user);

        if (gcParam)
            dto = null;// help GC
        return user;
    }


    /**
     * 将 User 转化成 UserDto
     * @param user
     * @return
     */
    private UserInfo convertUser2UserDto(User user, boolean gcParam) {
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(user, userInfo);
        if (gcParam)
            user = null; // help GC
        return userInfo;
    }
}
